package com.ux.widget.helper;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;

import com.learn.animation.R;
import com.ux.widget.ResideMenuView;
import com.ux.widget.ResideMenuView.Callback;


public class ResideMenuDragHelper {
	
	final int RESIDE_ANIMATION_DURATION = 400;
	
	int mScreenWidth, mScreenHeight;
	private ViewGroup mRootView;
	private ViewGroup uxResideMainContentView;
	private View uxResideMainViewDragZoneDetectorLeft, uxResideMainViewDragZoneDetectorRight;
	private View uxResideLeftContentView, uxResideRightContentView;
	//private GlassView uxGlassView;
	private Context mContext;
	private Callback mCallback;
	public void registerCallbackEvent(Callback callback) {
		mCallback = callback;
	}
	
	float lastActionDownX, lastActionDownY, lastActionDownCorrectX, lastRawX;
	/*
	|---------------------------------------|
	|							|			|
	|__LEFT_MENU_OPENED_WIDTH___|			|
	|							|			|
	|										|
	|	|									|
	|	|______RIGHT_MENU_OPENED_WIDTH______|
	|	|									|
	|										|
	|		|						|		|
	|__[*]__|						|__[*]__|
	|		|						|		|
	|	[*]	- DRAG_ZONE_DETECTOR_WIDTH		|
	|										|
	|---------------------------------------|
	 */
	int LEFT_MENU_OPENED_WIDTH, RIGHT_MENU_OPENED_WIDTH, DRAG_ZONE_DETECTOR_WIDTH, RIGHT_MENU_CONTENT_WIDTH;
	
	/** the flag of menu open status     */
    private boolean              isOpened = false;
    public boolean isOpened() {
    	return isOpened;
    }
    
    /**valid scale factor is between 0.0f and 1.0f.*/
    private float mScaleValue = 0.85f;
	
    private MotionEventState mMotionEventState = MotionEventState.STATE_INVALID_POINTER;
    private DragDirection mDragDirection;
    
    public static enum MotionEventState {
    	STATE_INVALID_POINTER, 
    	STATE_PRESSED_DOWN, 
    	STATE_DRAGGING_HORIZONTAL, 
    	STATE_DRAGGING_VERTICAL,
    	STATE_DRAGGING_DONE
    }
	public enum DragDirection {
		DRAG_DIRECTION_FROM_LEFT, 
		DRAG_DIRECTION_FROM_RIGHT,
		DRAG_DIRECTION_UP,
		DRAG_DIRECTION_DOWN
	}
	
	public static ResideMenuDragHelper create(ViewGroup rootView, ResideMenuView.Callback cb) {
		if (rootView == null) {
            throw new IllegalArgumentException("GroupRootView cannot be null");
        }
        return new ResideMenuDragHelper(rootView.getContext(), rootView, cb);
    }
	
	private ResideMenuDragHelper(Context context, ViewGroup rootView, ResideMenuView.Callback cb) {
		if (rootView == null) {
            throw new IllegalArgumentException("GroupRootView cannot be null");
        }
		if (context == null) {
			throw new IllegalArgumentException("Context cannot be null");
		}
        /*if (cb == null) {
            throw new IllegalArgumentException("Callback may not be null");
        }*/
		
		mContext = context;

		mRootView = rootView;
		
		uxResideMainViewDragZoneDetectorLeft = mRootView.findViewById(R.id.uxResideMainViewDragZoneDetectorLeft);
		uxResideMainViewDragZoneDetectorRight = mRootView.findViewById(R.id.uxResideMainViewDragZoneDetectorRight);
		uxResideMainContentView = (ViewGroup) mRootView.findViewById(R.id.uxResideMainViewRoot);
		
		uxResideLeftContentView = mRootView.findViewById(R.id.uxResideLeftView);
		uxResideRightContentView = mRootView.findViewById(R.id.uxResideRightView);
		
		//uxGlassView = (GlassView) mRootView.findViewById(R.id.uxGlassView);
		
        mCallback = cb;
        
        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
        mScreenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
        
        initAttributes();
	}
	
	private void initAttributes() {
		DRAG_ZONE_DETECTOR_WIDTH = (int) mScreenWidth / 15;
        
        LEFT_MENU_OPENED_WIDTH = mScreenWidth - ((int) (mScreenWidth + mScaleValue) / 4);
        RIGHT_MENU_OPENED_WIDTH = mScreenWidth - ((int) mScreenWidth / 9);
        
        // init for others Views
        RIGHT_MENU_CONTENT_WIDTH =  RIGHT_MENU_OPENED_WIDTH;
        
        updateRightMenuContentWidth();
        updateLeftMenuContentWidth();
        
		SCALE_CONST = mScaleValue / (4 + mScaleValue);
		
		//uxGlassView.setVisibility(View.GONE);
		//uxGlassView.setBlurRadius(15.0f);
	}
	
	public void updateRightMenuContentWidth() {
		uxResideRightContentView.getLayoutParams().width = RIGHT_MENU_CONTENT_WIDTH;
	}
	
	public void updateLeftMenuContentWidth() {
		//uxResideLeftContentView.getLayoutParams().width = LEFT_MENU_OPENED_WIDTH;
	}
	
	public void openMenu(DragDirection direction) {
		_playOpenMenu(direction);
	}
	private void _playOpenMenu(DragDirection direction) {
		isOpened = true;
		mDragDirection = direction;
		displayMenuView();
		AnimatorSet openAnim = buildTranslateOpenAnimation(uxResideMainContentView, direction);
		openAnim.addListener(_openAnimatorListener);
 		openAnim.start();
	}
	private void _playCloseMenu() {
		isOpened = false;
		AnimatorSet closeAnim = buildTranslateCloseAnimation(uxResideMainContentView);
		closeAnim.addListener(_openAnimatorListener);
		closeAnim.start();
	}
	private void openOrCloseMenu() {
		float currentX = uxResideMainContentView.getX();
		if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
			if (!isOpened && currentX > (mScreenWidth - LEFT_MENU_OPENED_WIDTH)) {
				_playOpenMenu(mDragDirection);
			} else {
				_playCloseMenu();
			}
			
		} else if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
			if (!isOpened && currentX < -(mScreenWidth - RIGHT_MENU_OPENED_WIDTH)) {
				_playOpenMenu(mDragDirection);
			}else {
				_playCloseMenu();
			}
		}
	}
	
	public void closeOpenedResideMenu() {
		if (isOpened) {
			_playCloseMenu();
		}
	}
	
	//private View viewAnimate;
	private boolean isResideAnimationPlaying = false;
	/**
     * Process a touch event received by the parent view. This method will dispatch callback events
     * as needed before returning. The parent view's onTouchEvent implementation should call this.
     *
     * @param ev The touch event received by the parent view
     */
    public boolean processTouchEvent(MotionEvent ev, DragDirection direction) {
    	if (isResideAnimationPlaying) {
    		return false;
    	}
    	mDragDirection = direction;
    	
    	setScaleDirectionByRawX(ev.getRawX(), false);
    	
		//switch (MotionEventCompat.getActionMasked(ev)) {
    	switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN:
				/*// Reset things for a new event stream, just in case we didn't get
				// the whole previous stream.
				// cancel();
				if (mVelocityTracker == null) {
					mVelocityTracker = VelocityTracker.obtain();
				}
				mVelocityTracker.addMovement(ev);*/

				lastActionDownX 	= ev.getX();
                lastActionDownY 	= ev.getY();
                
                if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
                	lastActionDownCorrectX = mScreenWidth - getDragZoneDetectorWidth();
                }
                
            	mMotionEventState   = MotionEventState.STATE_PRESSED_DOWN;
				break;
				
			/*case MotionEvent.ACTION_POINTER_DOWN:
			case MotionEvent.ACTION_POINTER_UP:
				// if we want to support multi-touch, plz place your code here
				break;*/
		
			case MotionEvent.ACTION_UP:
				if (mMotionEventState != MotionEventState.STATE_DRAGGING_HORIZONTAL) {
					mMotionEventState = MotionEventState.STATE_DRAGGING_DONE;
					if (isOpened) {// ensure to close opened menu in case we just touch DragDetectorView
						openOrCloseMenu();
					}
                	break;
                }
				mMotionEventState = MotionEventState.STATE_DRAGGING_DONE;
				openOrCloseMenu();
				break;
				
			case MotionEvent.ACTION_MOVE://TODO
				if(mMotionEventState != MotionEventState.STATE_PRESSED_DOWN 
					&& mMotionEventState != MotionEventState.STATE_DRAGGING_HORIZONTAL) {
					break;
				}
				
				float xOffset = (ev.getX() - lastActionDownX);
                float yOffset = (ev.getY() - lastActionDownY);
                
                // detect press and move direction
                // only allow drag View following horizontal direction
                if(mMotionEventState == MotionEventState.STATE_PRESSED_DOWN ) {
                	//Log.d("mapp", "[INF] >>> PRESSED_DOWN::xOffset="+xOffset+" - yOffset="+yOffset);
                    if(yOffset > 5 || yOffset < -5) {
                    	mMotionEventState = MotionEventState.STATE_DRAGGING_VERTICAL;
                    }
                    if(xOffset < -5 || xOffset > 5) {
                    	mMotionEventState = MotionEventState.STATE_DRAGGING_HORIZONTAL;
                        //ev.setAction(MotionEvent.ACTION_CANCEL);
                    }
                }
                
                // process move View
                if(mMotionEventState == MotionEventState.STATE_DRAGGING_HORIZONTAL) {
//                    if (currentActivityScaleX < 0.95)
//                        showScrollViewMenu(scrollViewMenu);
                	
                	displayMenuView();

                	float deltaX = ev.getRawX() - lastActionDownX;
                	//Log.d("mapp", "[INF] >>> PRESSED_DOWN::deltaX="+deltaX+" getX="+uxResideMainView.getX());
                	if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
                		
                		// translate Views
                		if (deltaX > LEFT_MENU_OPENED_WIDTH) {
                			deltaX = LEFT_MENU_OPENED_WIDTH;
                		} else if (deltaX < 0){
                			deltaX = 0;
                		}
                		uxResideMainContentView.setX(deltaX);
                		
                		// scale Views
                		float targetScale = getTargetScale(ev.getRawX());
                        //Log.d("mapp", "[INF] >>> ACTION_MOVE::targetScale="+targetScale);
                        uxResideMainContentView.setScaleX(targetScale);
                        uxResideMainContentView.setScaleY(targetScale);
                        
                        // play glass effect
                        /*if (scaleDirection ==  DragDirection.DRAG_DIRECTION_FROM_LEFT) {
                        	//uxGlassView.getBlurRadius();
                        	uxGlassView.setBlurRadius(uxGlassView.getBlurRadius() + (targetScale * 3));
                        } else if (scaleDirection ==  DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
                        	uxGlassView.setBlurRadius(uxGlassView.getBlurRadius() - (targetScale * 3));
                        }
                        Log.d("mapp", "[INF] >>> ACTION_MOVE::getBlurRadius="+uxGlassView.getBlurRadius());*/
                        lastRawX = ev.getRawX();
                        return true;
                	} else if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
                		float _deltaX = (lastActionDownCorrectX - deltaX);
                		//Log.d("mapp", "[INF] >>> PRESSED_DOWN::deltaX="+(-_deltaX)+" getX="+mParentView.getX());
                		if (_deltaX > RIGHT_MENU_OPENED_WIDTH) {
                			if (!isOpened)
                				_deltaX = RIGHT_MENU_OPENED_WIDTH;
                			else 
                				_deltaX = -deltaX;
                		} else if (_deltaX < 0) {
                			_deltaX = 0;
                		}
                		uxResideMainContentView.setX(-_deltaX);
                		
                		lastRawX = ev.getRawX();
                		return true;
                	}
                    
                    return true;
                }
				
				break;
		}
		lastRawX = ev.getRawX();
    	return false;
    }
    
    private DragDirection scaleDirection;
    private void setScaleDirectionByRawX(float currentRawX, boolean enableSetPivot){
        if (currentRawX < lastRawX) {
        	scaleDirection = DragDirection.DRAG_DIRECTION_FROM_RIGHT;
        } else {
        	scaleDirection = DragDirection.DRAG_DIRECTION_FROM_LEFT;
        }
    }
    private void displayMenuView() {
    	if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
    		uxResideLeftContentView.setVisibility(View.VISIBLE);
        	uxResideRightContentView.setVisibility(View.INVISIBLE);    		
    	} else if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
    		uxResideRightContentView.setVisibility(View.VISIBLE);
        	uxResideLeftContentView.setVisibility(View.INVISIBLE);    		
    	}
    }
    
    float SCALE_CONST;
    private float getTargetScale(float currentRawX){
    	float deltaScaleX = ((currentRawX - lastRawX) * (mScaleValue) * SCALE_CONST / LEFT_MENU_OPENED_WIDTH);
    	float currentScaleX = uxResideMainContentView.getScaleX();
    	float targetScaleX = 0;

    	// reset if exceed the scale limit
    	if (scaleDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
    		targetScaleX = currentScaleX - deltaScaleX;
    		targetScaleX = (targetScaleX < mScaleValue) ? mScaleValue : targetScaleX;
    	} else if (scaleDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
    		targetScaleX = currentScaleX + -deltaScaleX;
    		targetScaleX  = (targetScaleX > 1.0f) ? 1.0f : targetScaleX;
    	}
        
        return targetScaleX;

    }
    
    public int getDragZoneDetectorWidth() {
    	return DRAG_ZONE_DETECTOR_WIDTH;
    }
    
//    /**
//     * a helper method to build scale down animation;
//     *
//     * @param target
//     * @param targetScaleX
//     * @param targetScaleY
//     * @return
//     */
//    private AnimatorSet buildScaleDownAnimation(View target,float targetScaleX,float targetScaleY){
//
//        AnimatorSet scaleDown = new AnimatorSet();
//        scaleDown.playTogether(
//                ObjectAnimator.ofFloat(target, "scaleX", targetScaleX),
//                ObjectAnimator.ofFloat(target, "scaleY", targetScaleY)
//        );
//
//        /*scaleDown.setInterpolator(AnimationUtils.loadInterpolator(activity,
//                android.R.anim.decelerate_interpolator));*/
//		// Increasing factor above 1.0f makes exaggerates the
//		// ease-out effect (i.e., it starts even faster and ends evens slower)
//        scaleDown.setInterpolator(new DecelerateInterpolator(1.0f));
//        scaleDown.setDuration(1000);
//        return scaleDown;
//    }
    
//    /**
//     * a helper method to build scale up animation;
//     *
//     * @param target
//     * @param targetScaleX
//     * @param targetScaleY
//     * @return
//     */
//    private AnimatorSet buildScaleUpAnimation(View target,float targetScaleX,float targetScaleY){
//
//        AnimatorSet scaleUp = new AnimatorSet();
//		scaleUp.playTogether(
//        		ObjectAnimator.ofFloat(target, "scaleX", targetScaleX),
//        		ObjectAnimator.ofFloat(target, "scaleY", targetScaleY)
//        );
//
//        scaleUp.setDuration(1000);
//        return scaleUp;
//    }
    
    private Animator.AnimatorListener _openAnimatorListener = new Animator.AnimatorListener() {
		
		@Override
		public void onAnimationStart(Animator animation) {
			isResideAnimationPlaying = true;
		}
		
		@Override
		public void onAnimationEnd(Animator animation) {
			if (isOpened) { // the reside menu is opened
				
				if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
					uxResideMainViewDragZoneDetectorLeft.getLayoutParams().width = mScreenWidth;
					uxResideMainViewDragZoneDetectorLeft.requestLayout();
				} else if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
					uxResideMainViewDragZoneDetectorRight.getLayoutParams().width = mScreenWidth;
					uxResideMainViewDragZoneDetectorRight.requestLayout();
				}
				// fire callback event
				if (mCallback != null) {
					mCallback.onMenuOpened(mDragDirection);
				}
			} else { // the reside menu is closed
				if (mCallback != null) {
					mCallback.onMenuClosed();
				}
				
				if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_LEFT) {
					uxResideMainViewDragZoneDetectorLeft.getLayoutParams().width = DRAG_ZONE_DETECTOR_WIDTH;
					uxResideMainViewDragZoneDetectorLeft.requestLayout();
				} else if (mDragDirection == DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
					uxResideMainViewDragZoneDetectorRight.getLayoutParams().width = DRAG_ZONE_DETECTOR_WIDTH;
					uxResideMainViewDragZoneDetectorRight.requestLayout();
				}
				
				// hide all menu
				uxResideLeftContentView.setVisibility(View.INVISIBLE);
				uxResideRightContentView.setVisibility(View.INVISIBLE);
			}
			isResideAnimationPlaying = false;
		}
		
		@Override
		public void onAnimationCancel(Animator animation) {
			// not handle
		}
		
		@Override
		public void onAnimationRepeat(Animator animation) {
			// not handle
		}
		
	};
	
//	private AnimatorSet buildGlassAlphaAnimation(boolean show) {
//		uxGlassView.setVisibility(show ? View.VISIBLE : View.GONE);
//		AnimatorSet alphaSet = new AnimatorSet();
//		float alpha = show ? 1.0f : 0.0f;
//		alphaSet.playTogether(
//                ObjectAnimator.ofFloat(uxGlassView, "alpha", alpha)
//        );
//		alphaSet.setDuration(4000);
//		return alphaSet;
//	}
    private AnimatorSet buildTranslateOpenAnimation(View target, DragDirection direction){
    	AnimatorSet restoreSet = new AnimatorSet();
    	
    	if (direction ==  DragDirection.DRAG_DIRECTION_FROM_LEFT) {
    		restoreSet.playTogether(
    				ObjectAnimator.ofFloat(target, "X", LEFT_MENU_OPENED_WIDTH),
    				ObjectAnimator.ofFloat(target, "scaleX", mScaleValue),
    				ObjectAnimator.ofFloat(target, "scaleY", mScaleValue));
    	} else if (direction ==  DragDirection.DRAG_DIRECTION_FROM_RIGHT) {
    		restoreSet.playTogether(ObjectAnimator.ofFloat(target, "X", -RIGHT_MENU_OPENED_WIDTH));
    	}
    	
    	restoreSet.setDuration(RESIDE_ANIMATION_DURATION);
        return restoreSet;
    }
    private AnimatorSet buildTranslateCloseAnimation(View target){
    	AnimatorSet restoreSet = new AnimatorSet();

		restoreSet.playTogether(
				ObjectAnimator.ofFloat(target, "X", 0.0f),
				ObjectAnimator.ofFloat(target, "scaleX", 1.0f),
				ObjectAnimator.ofFloat(target, "scaleY", 1.0f));
		restoreSet.setInterpolator(new DecelerateInterpolator(1.5f));

    	restoreSet.setDuration(RESIDE_ANIMATION_DURATION);
        return restoreSet;
    }
    
    /**
	 * Convert Dp to Pixel
	 */
	public int dpToPx(float dp) {
		float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
				mContext.getResources().getDisplayMetrics());
		return (int) px;
	}

}
